/**
*
Java Diagram Package; An extremely flexible and fast multipurpose diagram
component for Swing.
Copyright (C) 2001 Eric Crahen <crahen@cse.buffalo.edu>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package diagram.tool;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import javax.swing.SwingUtilities;
import javax.swing.event.MouseInputAdapter;
import diagram.Diagram;
import diagram.DiagramUI;
import diagram.Figure;
import diagram.Link;
import diagram.figures.FigureBorder;
/**
* @class FigureShapping
*
* @date 08-20-2001
* @author Eric Crahen
* @version 1.0
*
* Add node shaping support to a Diagram.
*/
public class FigureShappingTool extends AbstractTool {
// Cache some frequently used Cursors
private static final Cursor HZ_CURSOR = new Cursor(Cursor.W_RESIZE_CURSOR);
private static final Cursor VT_CURSOR = new Cursor(Cursor.N_RESIZE_CURSOR);
private static final Cursor SENW_CURSOR = new Cursor(Cursor.NW_RESIZE_CURSOR);
private static final Cursor SWNE_CURSOR = new Cursor(Cursor.SW_RESIZE_CURSOR);
// Sizing directions
private static final int QUAD_LEFT = 0x01;
private static final int QUAD_MIDDLE = 0x02;
private static final int QUAD_RIGHT = 0x04;
private static final int QUAD_TOP = 0x100;
private static final int QUAD_CENTER = 0x200;
private static final int QUAD_BOTTOM = 0x400;
// Minimum size
private static final int MINIMUM_SIZE = 0x10;
private MouseHandler mouseHandler = new MouseHandler();
// Graphics context to paint on
private Graphics2D graphics;
// Working data
private Point2D dragPoint;
private Point2D lastPoint;
private Rectangle2D.Double outline = new Rectangle2D.Double();
private Cursor originalCursor = null;
private Diagram diagram;
// Selected figure info
private Point2D center;
private Rectangle2D bounds;
private Figure figure = null;
protected Figure[] relatedFigures = new Figure[4];
private int sizingDirection = -1;
/**
* Install the support in the specified Diagram
*
* @param Diagram
*/
public void install(Diagram diagram) {
diagram.addMouseListener(mouseHandler);
diagram.addMouseMotionListener(mouseHandler);
}
/**
* Uninstall the support from specified Diagram
*
* @param Diagram
*/
public void uninstall(Diagram diagram) {
diagram.removeMouseListener(mouseHandler);
diagram.removeMouseMotionListener(mouseHandler);
reset();
}
/**
* @class MouseHandler
*/
protected class MouseHandler extends MouseInputAdapter {
/**
* Called when dragging could possibly begin. If the coordinated of this
* event fall within the boundaries of some object in the Diagram
* then a drag is started.
*
* @param MouseEvent
*
* @pre MouseEvent must not have been consumed.
*/
public void mousePressed(MouseEvent e) {
// Check for left mouse button & a valid selection
if(e.isConsumed() || !SwingUtilities.isLeftMouseButton(e))
return;
diagram = (Diagram)e.getSource();
Point2D pt = e.getPoint();
// Find the figure which was clicked on
// DiagramModel diagramModel = diagram.getModel();
figure = (Figure)diagram.findFigure(pt);
if(figure == null || (figure instanceof Link)) {
reset();
return;
}
// Get the bounds & figure info
bounds = figure.getBounds2D(bounds);
center = figure.getCenter(center);
// Check the click point to see if it lies on the boundary of the figure
if(!FigureBorder.isBorderPoint(figure, pt)) {
reset();
return;
}
e.consume();
fireToolStarted();
// Get a drag outline setup
outline.setFrame(bounds);
dragPoint = pt;
getQuadrant(dragPoint);
// Change the cursor
originalCursor = diagram.getCursor();
diagram.setCursor(getCursor());
// Save the graphics
graphics = getGraphics(diagram);
// Get related figures
DiagramUI ui = (DiagramUI)diagram.getUI();
relatedFigures = ui.getConnected(figure, relatedFigures);
}
/**
* Called as the dragging occurs.
*
* @param MouseEvent
*/
public void mouseDragged(MouseEvent e) {
// Check for a valid drag message
if(dragPoint == null)
return;
Point2D pt = e.getPoint();
double dx = pt.getX() - dragPoint.getX();
double dy = pt.getY() - dragPoint.getY();
paintOutline(dx, dy, false);
lastPoint = pt;
}
/**
* Called when the dragging to stops. The Figure that was
* being dragged is moved to its new locations.
*
* @param MouseEvent
*/
public void mouseReleased(MouseEvent e) {
if(dragPoint != null) {
if(lastPoint != null) {
// Erase left over outline
paintOutline(0, 0, true);
// Reshape the figure
figure.setBounds(outline.getX(), outline.getY(),
outline.getWidth(), outline.getHeight());
}
// Damage related figures
DiagramUI ui = (DiagramUI)diagram.getUI();
Figure[] related = relatedFigures;
for(int j=0; j < related.length && related[j] != null; j++)
ui.damageFigure(related[j]);
// Reset
((DiagramUI)diagram.getUI()).refreshFigure(figure);
reset();
fireToolFinished();
}
}
}
/**
* Paint the resizing outline of the Figure.
*
* @param dx - position change
* @param dy - position change
*
* @param boolean true if only need to erase last drawn outline
*/
private final void paintOutline(double dx, double dy, boolean eraseOnly) {
// Erase last outline, if any
if(lastPoint != null)
graphics.drawRect((int)outline.x, (int)outline.y, (int)outline.width, (int)outline.height);
// Move & draw the dragout line
if(!eraseOnly && !(dx == 0 && dy == 0)) {
reshapeOutline(dx, dy);
graphics.drawRect((int)outline.x, (int)outline.y, (int)outline.width, (int)outline.height);
}
((DiagramUI)diagram.getUI()).damageFigure(figure);
}
/**
* Reshape the drag outline.
*
* @param double
* @param double
*/
private final void reshapeOutline(double dx, double dy) {
int min = MINIMUM_SIZE;
double t;
if(hasQuad(QUAD_LEFT)) {
if((t = bounds.getWidth() - dx) > min) {
outline.x = bounds.getX() + dx;
outline.width = t;
}
} else if(hasQuad(QUAD_RIGHT))
if((t = bounds.getWidth() + dx) > min)
outline.width = t;
if(hasQuad(QUAD_TOP)) {
if((t = bounds.getHeight() - dy) > min) {
outline.y = bounds.getY() + dy;
outline.height = t;
}
} else if(hasQuad(QUAD_BOTTOM))
if((t = bounds.getHeight() + dy) > min)
outline.height = t;
}
/**
* Find the direction using the clicked point & the center of
* the target Figure.
*
* @param Point2D clicked point
*/
private final void getQuadrant(Point2D pt) {
double padx = bounds.getWidth() / 8d;
double pady = bounds.getHeight() / 8d;
double x = pt.getX();
double y = pt.getY();
double centerX = center.getX();
double centerY = center.getY();
int dir;
if(x < centerX - padx)
dir = QUAD_LEFT;
else if(x < centerX + padx)
dir = QUAD_MIDDLE;
else
dir = QUAD_RIGHT;
if(y < centerY - pady)
dir |= QUAD_TOP;
else if(y < centerY + pady)
dir |= QUAD_CENTER;
else
dir |= QUAD_BOTTOM;
sizingDirection = dir;
}
/**
* Test the quadrant flag
*
* @return boolean
*/
private final boolean hasQuad(int quad) {
return ((sizingDirection & quad) == quad);
}
/**
* Guess the right cursor to use based on some point in the Figure
*
* @return Cursor
*/
private final Cursor getCursor() {
// Left/Right only
if(hasQuad(QUAD_CENTER|QUAD_LEFT) || hasQuad(QUAD_CENTER|QUAD_RIGHT))
return HZ_CURSOR;
// Top/Bottom only
if(hasQuad(QUAD_MIDDLE|QUAD_TOP) || hasQuad(QUAD_MIDDLE|QUAD_BOTTOM))
return VT_CURSOR;
// Diagonals
if(hasQuad(QUAD_BOTTOM|QUAD_LEFT) || hasQuad(QUAD_TOP|QUAD_RIGHT))
return SWNE_CURSOR;
return SENW_CURSOR;
}
/**
* Prepare the graphcis context.
*
* @return Graphics2D
*/
final private Graphics2D getGraphics(Component c) {
Graphics2D g = (Graphics2D)c.getGraphics();
g.setColor(Color.gray);
g.setXORMode(c.getBackground());
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
return g;
}
protected void reset() {
if(diagram != null && originalCursor != null)
diagram.setCursor(originalCursor);
originalCursor = null;
diagram = null;
figure = null;
graphics = null;
lastPoint = dragPoint = null;
java.util.Arrays.fill(relatedFigures, 0, relatedFigures.length, null);
}
}